﻿using System;
using System.IO;
using System.Text;
using System.Security.Cryptography;
using UnityEngine;

#if UNITY_EDITOR
using UnityEditor;
#endif

namespace Z1029.PhiPlayer.OpenSource
{
    public class Utils
    {
        /*
        [Obsolete("请使用没有Nullable的方法")]
        public static float? GetValueFromTimeAndValue(double now, float startTime, float endTime, float startVal, float endVal)
        {
            if (now > startTime && now < endTime)
            {
                double num = (now - startTime) / (endTime - startTime);
                num = (endVal - startVal) * num + startVal;
                return (float)num;
            }
            return null;
        }
        */

        public static float GetValueFromTimeAndValue(double now, float startTime, float endTime, float startVal, float endVal)
        {
            if (now > startTime && now < endTime)
            {
                double num = (now - startTime) / (endTime - startTime);
                num = (endVal - startVal) * num + startVal;
                return (float)num;
            }
            return endVal;
        }

        public static double GetTimeFromBeatTime(float beatTime, float bpm)
        {
            double time = beatTime * (60 / bpm) / 32;
            if (time > 1024) time = 1024;//限制数值大小，避免出现错误（音乐不可能超过17.67分钟）
            return time;
        }

        public static int GetBeatTimeFromTime(double time, float bpm)
        {
            int btime = Mathf.RoundToInt((float)time * 32 / (60 / bpm));
            if (btime > 32767) btime = 32767;//限制数值大小，避免出现错误（同时防止bpm为0错误）
            return btime;
        }

        public static void AndroidToastStringShow(object str, AndroidJavaObject activity = null)
        {
            if (Application.platform != RuntimePlatform.Android)
            {
                Debug.Log(str);
                return;
            }
            if (activity == null)
            {
                AndroidJavaClass UnityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
                activity = UnityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
            }

            AndroidJavaClass Toast = new AndroidJavaClass("android.widget.Toast");
            AndroidJavaObject context = activity.Call<AndroidJavaObject>("getApplicationContext");
            activity.Call("runOnUiThread", new AndroidJavaRunnable(() =>
            {
                AndroidJavaObject javaString = new AndroidJavaObject("java.lang.String", str.ToString());
                Toast.CallStatic<AndroidJavaObject>("makeText", context, javaString, Toast.GetStatic<int>("LENGTH_SHORT")).Call("show");
            }
            ));
        }
    }

    public class SecurityUtils
    {
        public static string Md5Sum(string strToEncrypt)
        {
            // 将需要加密的字符串转为byte数组
            byte[] bs = Encoding.UTF8.GetBytes(strToEncrypt);
            return Md5Sum(bs);
        }

        public static string Md5Sum(byte[] bytesToEncrypt)
        {
            // 创建md5 对象
            MD5 md5 = MD5.Create();

            // 生成16位的二进制校验码
            byte[] hashBytes = md5.ComputeHash(bytesToEncrypt);

            // 转为32位字符串
            string hashString = "";
            for (int i = 0; i < hashBytes.Length; i++)
            {
                hashString += System.Convert.ToString(hashBytes[i], 16).PadLeft(2, '0');
            }

            return hashString.PadLeft(32, '0');
        }
    }

    public class TransformUtils
    {

        public static Vector3 GetV3FromTimeAndValue(float now, float startTime, float endTime, Vector3 startVal, Vector3 endVal)
        {
            Vector3 result;
            result.x = (float)Utils.GetValueFromTimeAndValue(now, startTime, endTime, startVal.x, endVal.x);
            result.y = (float)Utils.GetValueFromTimeAndValue(now, startTime, endTime, startVal.y, endVal.y);
            result.z = (float)Utils.GetValueFromTimeAndValue(now, startTime, endTime, startVal.z, endVal.z);
            return result;
        }

        public static Vector3 GetPosFormEvent(TransformEvent te, float time)
        {
            return TransformUtils.GetV3FromTimeAndValue(time, te.startTime, te.endTime, te.startPos, te.endPos);
        }

        public static Vector3 GetRotV3FormEvent(TransformEvent te, float time)
        {
            return TransformUtils.GetV3FromTimeAndValue(time, te.startTime, te.endTime, te.startRot, te.endRot);
        }

        public static Quaternion GetRotFormEvent(TransformEvent te, float time)
        {
            return Quaternion.Euler(TransformUtils.GetRotV3FormEvent(te, time));
        }

        public static Vector3 GetPosFormEvents(TransformEvent[] te, float time)
        {
            for (int i = 0; i < te.Length; i++)
            {
                if (TransformUtils.GetPosFormEvent(te[i], time) != Vector3.zero)
                {
                    return TransformUtils.GetPosFormEvent(te[i], time);
                }
            }
            return Vector3.zero;
        }

        public static Vector3 GetRotV3FormEvents(TransformEvent[] te, float time)
        {
            for (int i = 0; i < te.Length; i++)
            {
                if (TransformUtils.GetRotV3FormEvent(te[i], time) != Vector3.zero)
                {
                    return TransformUtils.GetRotV3FormEvent(te[i], time);
                }
            }
            return Vector3.zero;
        }

        public static Quaternion GetRotFormEvents(TransformEvent[] te, float time)
        {
            return Quaternion.Euler(TransformUtils.GetRotV3FormEvents(te, time));
        }

        public static Vector3 GetLocalPosition(Vector3 worldPosition, Transform parent)
        {
            Vector3 localPosition = parent.InverseTransformPoint(worldPosition);
            return localPosition;
        }

        [Serializable]
        public struct TransformEvent
        {
            public float startTime, endTime;
            public Vector3 startPos, endPos, startRot, endRot;
        }
    }

    public class CompressUtil
    {
        //TODO
    }

    public class FileManager
    {
        public static string ResourcesPath = PlayerPrefs.GetString("ResourcesPath", Application.persistentDataPath + "/Tracks");
    }

    /// <summary>
    /// This code is from Dotween
    /// </summary>
    public class EaseUtility
    {
        public enum Ease
        {
            Linear,
            InSine,
            OutSine,
            InOutSine,
            InQuad,
            OutQuad,
            InOutQuad,
            InCubic,
            OutCubic,
            InOutCubic,
            InQuart,
            OutQuart,
            InOutQuart,
            InQuint,
            OutQuint,
            InOutQuint,
            InExpo,
            OutExpo,
            InOutExpo,
            InCirc,
            OutCirc,
            InOutCirc,
            InElastic,
            OutElastic,
            InOutElastic,
            InBack,
            OutBack,
            InOutBack,
            InBounce,
            OutBounce,
            InOutBounce,
            Flash,
            InFlash,
            OutFlash,
            InOutFlash
        }

        public static float Evaluate(Ease easeType, float time, float duration, float overshootOrAmplitude = 0, float period = 0)
        {
            switch (easeType)
            {
                case Ease.Linear:
                    return time / duration;
                case Ease.InSine:
                    return -(float)Math.Cos((double)(time / duration * 1.57079637f)) + 1f;
                case Ease.OutSine:
                    return (float)Math.Sin((double)(time / duration * 1.57079637f));
                case Ease.InOutSine:
                    return -0.5f * ((float)Math.Cos((double)(3.14159274f * time / duration)) - 1f);
                case Ease.InQuad:
                    return (time /= duration) * time;
                case Ease.OutQuad:
                    return -(time /= duration) * (time - 2f);
                case Ease.InOutQuad:
                    if ((time /= duration * 0.5f) < 1f)
                    {
                        return 0.5f * time * time;
                    }
                    return -0.5f * ((time -= 1f) * (time - 2f) - 1f);
                case Ease.InCubic:
                    return (time /= duration) * time * time;
                case Ease.OutCubic:
                    return (time = time / duration - 1f) * time * time + 1f;
                case Ease.InOutCubic:
                    if ((time /= duration * 0.5f) < 1f)
                    {
                        return 0.5f * time * time * time;
                    }
                    return 0.5f * ((time -= 2f) * time * time + 2f);
                case Ease.InQuart:
                    return (time /= duration) * time * time * time;
                case Ease.OutQuart:
                    return -((time = time / duration - 1f) * time * time * time - 1f);
                case Ease.InOutQuart:
                    if ((time /= duration * 0.5f) < 1f)
                    {
                        return 0.5f * time * time * time * time;
                    }
                    return -0.5f * ((time -= 2f) * time * time * time - 2f);
                case Ease.InQuint:
                    return (time /= duration) * time * time * time * time;
                case Ease.OutQuint:
                    return (time = time / duration - 1f) * time * time * time * time + 1f;
                case Ease.InOutQuint:
                    if ((time /= duration * 0.5f) < 1f)
                    {
                        return 0.5f * time * time * time * time * time;
                    }
                    return 0.5f * ((time -= 2f) * time * time * time * time + 2f);
                case Ease.InExpo:
                    if (time != 0f)
                    {
                        return (float)Math.Pow(2.0, (double)(10f * (time / duration - 1f)));
                    }
                    return 0f;
                case Ease.OutExpo:
                    if (time == duration)
                    {
                        return 1f;
                    }
                    return -(float)Math.Pow(2.0, (double)(-10f * time / duration)) + 1f;
                case Ease.InOutExpo:
                    if (time == 0f)
                    {
                        return 0f;
                    }
                    if (time == duration)
                    {
                        return 1f;
                    }
                    if ((time /= duration * 0.5f) < 1f)
                    {
                        return 0.5f * (float)Math.Pow(2.0, (double)(10f * (time - 1f)));
                    }
                    return 0.5f * (-(float)Math.Pow(2.0, (double)(-10f * (time -= 1f))) + 2f);
                case Ease.InCirc:
                    return -((float)Math.Sqrt((double)(1f - (time /= duration) * time)) - 1f);
                case Ease.OutCirc:
                    return (float)Math.Sqrt((double)(1f - (time = time / duration - 1f) * time));
                case Ease.InOutCirc:
                    if ((time /= duration * 0.5f) < 1f)
                    {
                        return -0.5f * ((float)Math.Sqrt((double)(1f - time * time)) - 1f);
                    }
                    return 0.5f * ((float)Math.Sqrt((double)(1f - (time -= 2f) * time)) + 1f);
                case Ease.InElastic:
                    {
                        if (time == 0f)
                        {
                            return 0f;
                        }
                        if ((time /= duration) == 1f)
                        {
                            return 1f;
                        }
                        if (period == 0f)
                        {
                            period = duration * 0.3f;
                        }
                        float num;
                        if (overshootOrAmplitude < 1f)
                        {
                            overshootOrAmplitude = 1f;
                            num = period / 4f;
                        }
                        else
                        {
                            num = period / 6.28318548f * (float)Math.Asin((double)(1f / overshootOrAmplitude));
                        }
                        return -(overshootOrAmplitude * (float)Math.Pow(2.0, (double)(10f * (time -= 1f))) * (float)Math.Sin((double)((time * duration - num) * 6.28318548f / period)));
                    }
                case Ease.OutElastic:
                    {
                        if (time == 0f)
                        {
                            return 0f;
                        }
                        if ((time /= duration) == 1f)
                        {
                            return 1f;
                        }
                        if (period == 0f)
                        {
                            period = duration * 0.3f;
                        }
                        float num2;
                        if (overshootOrAmplitude < 1f)
                        {
                            overshootOrAmplitude = 1f;
                            num2 = period / 4f;
                        }
                        else
                        {
                            num2 = period / 6.28318548f * (float)Math.Asin((double)(1f / overshootOrAmplitude));
                        }
                        return overshootOrAmplitude * (float)Math.Pow(2.0, (double)(-10f * time)) * (float)Math.Sin((double)((time * duration - num2) * 6.28318548f / period)) + 1f;
                    }
                case Ease.InOutElastic:
                    {
                        if (time == 0f)
                        {
                            return 0f;
                        }
                        if ((time /= duration * 0.5f) == 2f)
                        {
                            return 1f;
                        }
                        if (period == 0f)
                        {
                            period = duration * 0.450000018f;
                        }
                        float num3;
                        if (overshootOrAmplitude < 1f)
                        {
                            overshootOrAmplitude = 1f;
                            num3 = period / 4f;
                        }
                        else
                        {
                            num3 = period / 6.28318548f * (float)Math.Asin((double)(1f / overshootOrAmplitude));
                        }
                        if (time < 1f)
                        {
                            return -0.5f * (overshootOrAmplitude * (float)Math.Pow(2.0, (double)(10f * (time -= 1f))) * (float)Math.Sin((double)((time * duration - num3) * 6.28318548f / period)));
                        }
                        return overshootOrAmplitude * (float)Math.Pow(2.0, (double)(-10f * (time -= 1f))) * (float)Math.Sin((double)((time * duration - num3) * 6.28318548f / period)) * 0.5f + 1f;
                    }
                case Ease.InBack:
                    return (time /= duration) * time * ((overshootOrAmplitude + 1f) * time - overshootOrAmplitude);
                case Ease.OutBack:
                    return (time = time / duration - 1f) * time * ((overshootOrAmplitude + 1f) * time + overshootOrAmplitude) + 1f;
                case Ease.InOutBack:
                    if ((time /= duration * 0.5f) < 1f)
                    {
                        return 0.5f * (time * time * (((overshootOrAmplitude *= 1.525f) + 1f) * time - overshootOrAmplitude));
                    }
                    return 0.5f * ((time -= 2f) * time * (((overshootOrAmplitude *= 1.525f) + 1f) * time + overshootOrAmplitude) + 2f);
                case Ease.InBounce:
                    return Bounce.EaseIn(time, duration, overshootOrAmplitude, period);
                case Ease.OutBounce:
                    return Bounce.EaseOut(time, duration, overshootOrAmplitude, period);
                case Ease.InOutBounce:
                    return Bounce.EaseInOut(time, duration, overshootOrAmplitude, period);
                case Ease.Flash:
                    return Flash.Ease(time, duration, overshootOrAmplitude, period);
                case Ease.InFlash:
                    return Flash.EaseIn(time, duration, overshootOrAmplitude, period);
                case Ease.OutFlash:
                    return Flash.EaseOut(time, duration, overshootOrAmplitude, period);
                case Ease.InOutFlash:
                    return Flash.EaseInOut(time, duration, overshootOrAmplitude, period);
                default:
                    return -(time /= duration) * (time - 2f);
            }
        }

        /// <summary>
        /// This class contains a C# port of the easing equations created by Robert Penner (http://robertpenner.com/easing).
        /// </summary>
        public static class Bounce
        {
            /// <summary>
            /// Easing equation function for a bounce (exponentially decaying parabolic bounce) easing in: accelerating from zero velocity.
            /// </summary>
            /// <param name="time">
            /// Current time (in frames or seconds).
            /// </param>
            /// <param name="duration">
            /// Expected easing duration (in frames or seconds).
            /// </param>
            /// <param name="unusedOvershootOrAmplitude">Unused: here to keep same delegate for all ease types.</param>
            /// <param name="unusedPeriod">Unused: here to keep same delegate for all ease types.</param>
            /// <returns>
            /// The eased value.
            /// </returns>
            public static float EaseIn(float time, float duration, float unusedOvershootOrAmplitude, float unusedPeriod)
            {
                return 1f - Bounce.EaseOut(duration - time, duration, -1f, -1f);
            }

            /// <summary>
            /// Easing equation function for a bounce (exponentially decaying parabolic bounce) easing out: decelerating from zero velocity.
            /// </summary>
            /// <param name="time">
            /// Current time (in frames or seconds).
            /// </param>
            /// <param name="duration">
            /// Expected easing duration (in frames or seconds).
            /// </param>
            /// <param name="unusedOvershootOrAmplitude">Unused: here to keep same delegate for all ease types.</param>
            /// <param name="unusedPeriod">Unused: here to keep same delegate for all ease types.</param>
            /// <returns>
            /// The eased value.
            /// </returns>
            public static float EaseOut(float time, float duration, float unusedOvershootOrAmplitude, float unusedPeriod)
            {
                if ((time /= duration) < 0.363636374f)
                {
                    return 7.5625f * time * time;
                }
                if (time < 0.727272749f)
                {
                    return 7.5625f * (time -= 0.545454562f) * time + 0.75f;
                }
                if (time < 0.909090936f)
                {
                    return 7.5625f * (time -= 0.8181818f) * time + 0.9375f;
                }
                return 7.5625f * (time -= 0.954545438f) * time + 0.984375f;
            }

            /// <summary>
            /// Easing equation function for a bounce (exponentially decaying parabolic bounce) easing in/out: acceleration until halfway, then deceleration.
            /// </summary>
            /// <param name="time">
            /// Current time (in frames or seconds).
            /// </param>
            /// <param name="duration">
            /// Expected easing duration (in frames or seconds).
            /// </param>
            /// <param name="unusedOvershootOrAmplitude">Unused: here to keep same delegate for all ease types.</param>
            /// <param name="unusedPeriod">Unused: here to keep same delegate for all ease types.</param>
            /// <returns>
            /// The eased value.
            /// </returns>
            public static float EaseInOut(float time, float duration, float unusedOvershootOrAmplitude, float unusedPeriod)
            {
                if (time < duration * 0.5f)
                {
                    return Bounce.EaseIn(time * 2f, duration, -1f, -1f) * 0.5f;
                }
                return Bounce.EaseOut(time * 2f - duration, duration, -1f, -1f) * 0.5f + 0.5f;
            }
        }

        public static class Flash
        {
            public static float Ease(float time, float duration, float overshootOrAmplitude, float period)
            {
                int num = Mathf.CeilToInt(time / duration * overshootOrAmplitude);
                float num2 = duration / overshootOrAmplitude;
                time -= num2 * (float)(num - 1);
                float num3 = (float)((num % 2 != 0) ? 1 : -1);
                if (num3 < 0f)
                {
                    time -= num2;
                }
                float res = time * num3 / num2;
                return Flash.WeightedEase(overshootOrAmplitude, period, num, num2, num3, res);
            }

            public static float EaseIn(float time, float duration, float overshootOrAmplitude, float period)
            {
                int num = Mathf.CeilToInt(time / duration * overshootOrAmplitude);
                float num2 = duration / overshootOrAmplitude;
                time -= num2 * (float)(num - 1);
                float num3 = (float)((num % 2 != 0) ? 1 : -1);
                if (num3 < 0f)
                {
                    time -= num2;
                }
                time *= num3;
                float res = (time /= num2) * time;
                return Flash.WeightedEase(overshootOrAmplitude, period, num, num2, num3, res);
            }

            public static float EaseOut(float time, float duration, float overshootOrAmplitude, float period)
            {
                int num = Mathf.CeilToInt(time / duration * overshootOrAmplitude);
                float num2 = duration / overshootOrAmplitude;
                time -= num2 * (float)(num - 1);
                float num3 = (float)((num % 2 != 0) ? 1 : -1);
                if (num3 < 0f)
                {
                    time -= num2;
                }
                time *= num3;
                float res = -(time /= num2) * (time - 2f);
                return Flash.WeightedEase(overshootOrAmplitude, period, num, num2, num3, res);
            }

            public static float EaseInOut(float time, float duration, float overshootOrAmplitude, float period)
            {
                int num = Mathf.CeilToInt(time / duration * overshootOrAmplitude);
                float num2 = duration / overshootOrAmplitude;
                time -= num2 * (float)(num - 1);
                float num3 = (float)((num % 2 != 0) ? 1 : -1);
                if (num3 < 0f)
                {
                    time -= num2;
                }
                time *= num3;
                float res = ((time /= num2 * 0.5f) < 1f) ? (0.5f * time * time) : (-0.5f * ((time -= 1f) * (time - 2f) - 1f));
                return Flash.WeightedEase(overshootOrAmplitude, period, num, num2, num3, res);
            }

            private static float WeightedEase(float overshootOrAmplitude, float period, int stepIndex, float stepDuration, float dir, float res)
            {
                float num = 0f;
                float num2 = 0f;
                if (dir > 0f && (int)overshootOrAmplitude % 2 == 0)
                {
                    stepIndex++;
                }
                else if (dir < 0f && (int)overshootOrAmplitude % 2 != 0)
                {
                    stepIndex++;
                }
                if (period > 0f)
                {
                    float num3 = (float)Math.Truncate((double)overshootOrAmplitude);
                    num2 = overshootOrAmplitude - num3;
                    if (num3 % 2f > 0f)
                    {
                        num2 = 1f - num2;
                    }
                    num2 = num2 * (float)stepIndex / overshootOrAmplitude;
                    num = res * (overshootOrAmplitude - (float)stepIndex) / overshootOrAmplitude;
                }
                else if (period < 0f)
                {
                    period = -period;
                    num = res * (float)stepIndex / overshootOrAmplitude;
                }
                float num4 = num - res;
                res += num4 * period + num2;
                if (res > 1f)
                {
                    res = 1f;
                }
                return res;
            }
        }
    }
}